home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part2 / 16532 < prev    next >
Encoding:
Text File  |  1996-08-05  |  3.5 KB  |  106 lines

  1. Path: news.th-darmstadt.de!news
  2. From: enno@inferenzsysteme.informatik.th-darmstadt.de (Enno Sandner)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Mutually referring header files
  5. Date: 10 Apr 1996 19:31:52 +0200
  6. Organization: Fachbereich Informatik, TH Darmstadt
  7. Sender: enno@kitz.inferenzsysteme.informatik.th-darmstadt.de
  8. Message-ID: <lt91g4ouif.fsf@kitz.inferenzsysteme.informatik.th-darmstadt.de>
  9. References: <4ked7q$8g3@dub-news-svc-1.compuserve.com>
  10. NNTP-Posting-Host: kitz.intellektik.informatik.th-darmstadt.de
  11. In-reply-to: RossBoylan@aol.com's message of Tue, 09 Apr 1996 19:22:49 GMT
  12. X-Newsreader: Gnus v5.1
  13.  
  14. In article <4ked7q$8g3@dub-news-svc-1.compuserve.com> RossBoylan@aol.com (Ross Boylan) writes:
  15.  
  16.    I have 2 classes A + B, defined in separate files.  Each refers to and
  17.    messages the other.  What is the best way to handle this?
  18.  
  19.    1) My naive approach was
  20.    a.h----------------------------------------------------------
  21.    #ifndef ah
  22.    #define ah
  23.    #include "b.h:
  24.    class A {
  25.    public:
  26.        void hello();
  27.    private:
  28.        B *pB;
  29.    //etc
  30.    };
  31.    #endif
  32.  
  33.    b.h------------------------------------------------------------
  34.    #ifndef bh
  35.    #define bh
  36.    #include "a.h"
  37.    class B{
  38.    public:
  39.        void hello();
  40.    private:
  41.        A *pA;
  42.    };
  43.    #endif
  44.    -------------------------------------------------------------------
  45.    The #ifndef's prevent infinite recursion, but if we start with
  46.    #include "a.h"
  47.    this will then include "b.h"
  48.    b.h will skip a.h since the symbol ah is already defined.
  49.    So when we get to the line A *pA in b.h, A has not yet been defined
  50.    and we get an error.
  51.  
  52.    2) My less naive approach was to add the declaration
  53.    class A;
  54.    to b.h.  This works, but seems awfully obscure, since on the face of
  55.    it the code 
  56.    "include a.h"
  57.    class A; 
  58.    is redundant.
  59.    Also when you multiply this by a bunch of classes referencing each
  60.    other, it gets pretty messy.  In particular if you add a subclass of A
  61.    to the a.h file, and then want to refer to it from elsewhere, you must
  62.    add the declaration for this class to all the consumers.
  63.  
  64.  
  65.    3) It may or may not matter that I'm actually using smart pointers, so
  66.    the pointer declarations are actually
  67.    CountedObjPtr<A> pA;
  68.  
  69.    4) 
  70.    Also, I put no code definition in the header file (i.e., b.h contains
  71.    no lines of the form
  72.    class B {
  73.        void doit() {pA->hello();};
  74.    };
  75.    Such lines require more than class A;--they must actually know the
  76.    protocols A responds to.
  77.  
  78.    This bit of code discipline is possible for regular classes, but what
  79.    do I do for templates.  With my compiler (MSVC++ 4.0) the header file
  80.    must include all the code definition.
  81.  
  82. I see mainly two solutions:
  83. 1) Use forward declarations and put the appropriate include-command
  84.    in implementation not in the header-file.
  85. 2) Avoid cyclic dependencies by introducing abstract classes.
  86.    For instance, instead of let A maintain a pointer to the concrete
  87.    class 'B', ask yourself what kind of services 'A' uses from 'B'
  88.    and make them explicit in an suitable interface, which the class
  89.    'B' has to conform to.
  90.    Say 'A' uses the services 'void f1()' and 'void f2()' from 'B'.
  91.    The abstract class 'BBase' represents the interface available
  92.    for 'A' to use services from 'B'.
  93.  
  94.         class BBase {
  95.           public:
  96.            virtual ~BBase() {}
  97.            void f1()=0;
  98.            void f2()=0;
  99.         // BBase may need an additional Clone method
  100.             };
  101.  
  102.    'A' maintains a pointer to a BBase-object instead and 'B' conforms to
  103.    the BBase interface which is expressed by using public inheritence.
  104.  
  105.     Enno
  106.